package dessin;

import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;
import java.util.regex.Pattern;
import modele.Point;
import javax.swing.*;

import aLeSoftware.EnvironnementDessin;

import patternobserver.*;
import xdivers.Outils;
import modele.*;
/**
 * La classe ModeleDessin contient toutes les informations relatives au canevas du dessin en cours : 
 * sa dimension, son emplacement physique sur le système de fichier, une instance de la classe Groupe qui contient
 * les différentes formes géométriques qui composent le dessin, le pas de la grille de dessin, les coordonnées du pointeur sur le dessin.
 * @author Arnaud
 *
 */
public class ModeleDessin implements Serializable {

	/**
	 * Le Groupe de forme qui contient toutes les formes Geometriques du dessin
	 */
	private Groupe groupeDeFormes = new Groupe();
	private FormeGeometrique formeTampon = null;
	/**
	 * La dimension du dessin représenté par une instance de la classe Dimension
	 */
	private Dimension dimensionDessin;
	/**
	 * L'emplacement physique du dessin sur le system de fichier lorsque celui-ci a été enregistré. Par défaut il est égal à "New Picture".
	 */
	private String path = "New Picture";
	/**
	 * Les coordonnées du pointeur sur le canevas
	 */
	private String coordonnees;
	/**
	 * Le pas choisi pour la grille à pas réglable
	 */
	private int pas = 50;
	
	/**
	 * Constructeur par défaut prenant en parametre une dimension d
	 * @param d
	 */
	public ModeleDessin(Dimension d) {
		this.dimensionDessin =d;
		this.coordonnees = "0/0";
	}
	
	
	//Setters & Getters
	
	/**
	 * Retourne le path
	 * @return une instance de String correspondant au path
	 */
	public String getPath() {return path;}
	/**
	 * Permet de modifier le path
	 * @param path
	 */
	public void setPath(String path) {this.path = path;}
	
	/**
	 * Retourne le groupe
	 * @return le groupe
	 */
	public Groupe getGroupeDeFormes() {return groupeDeFormes;}

	/**
	 * Retourne la forme tampon
	 * @return une instance de FormeGeometrique qui correspond à la forme tampon actuelle du dessin
	 */
	public FormeGeometrique getFormeTampon() {return formeTampon;}
	
	/**
	 * Modifie la forme tampon
	 * @param formeTampon
	 */
	public void setFormeTampon(FormeGeometrique formeTampon) {this.formeTampon = formeTampon;}

	/**
	 * Retourne la dimension du dessin
	 * @return une instance de la classe Dimension
	 */
	public Dimension getDimensionDessin() {return dimensionDessin;}
	/**
	 * Modifie la dimension du dessin
	 * @param dimensionDessin
	 */
	public void setDimensionDessin(Dimension dimensionDessin) {this.dimensionDessin = dimensionDessin;}

	/**
	 * Retourne les coordonées du pointeur sur le canevas du dessin
	 * @return une instance de String correpondant aux coordonnées
	 */
	public String getCoordonnees() {return coordonnees;}
	/**
	 * Modifie les coordonées
	 * @param coordonnees
	 */
	public void setCoordonnees(String coordonnees) {this.coordonnees = coordonnees;}
	
	/**
	 * Retourne le pas de la grille du dessin
	 * @return le pas de la grille du dessin
	 */
	public int getPas() {return pas;}
	
	/**
	 * Modifie le pas de la grille du dessin
	 * @param pas
	 */
	public void setPas(int pas) {this.pas = pas;}

	/**
	 * Retourne le nom du fichier dans lequel est enregistré le dessin sur le systeme de fichier.
	 * Ce nom est obtenu à partir du chemin complet.
	 * @return le nom du fichier
	 */
	public String getName(){
		String separatorPath = File.separator;
		String name = path.split(Pattern.quote(separatorPath))[path.split(Pattern.quote(separatorPath )).length-1];
		return name;
	}


	
	/**
	 *Méthode pour effacer tout le dessin. Le groupe est remplacé par un nouveau groupe. La forme tampon est positionnée à null. 
	 */
	public void clear() {
		this.groupeDeFormes = new Groupe();
		this.formeTampon = null;
	}
	
	
	/**
	 * méthode pour sérializer le ModeleDessin
	 * @param path
	 */
	public void serializer(String path){
		FileOutputStream fos;
		try {
		fos = new FileOutputStream(path);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(this);
		oos.close();
		}
		catch(Exception e){
			System.out.println("Problem in save process"+e.getMessage());
		}
	}
	
	/**
	 * Méthode pour stocker la formeTampon dans le Groupe
	 */
	public void stockFormeTampon(){
		if (getFormeTampon() instanceof Polygone){
			Polygone poly = (Polygone) getFormeTampon();
			this.getGroupeDeFormes().addForme(poly);
			this.setFormeTampon(null);
			}
		
		if (getFormeTampon() instanceof Segment){
			Segment seg = (Segment) getFormeTampon();
			if (seg.getOrigine() != null && seg.getExtremite() !=null)
			{
			this.getGroupeDeFormes().addForme(seg);		
			}
			this.setFormeTampon(null);
		}
	}
	
	/*
	 * Retourne un entier qui indique le nombre de formes selectionnées dans le dessin
	 */
	public int howMuchSelected(){
		Groupe grp = getGroupeSelection();
		return grp.getlesFormes().size();
	}
	
	/**
	 * Méthode qui permet de remettre à zéro la selection : elle parcourt le Groupe et si une forme est selectionnée
	 * (si son attribut selected vaut TRUE) alors on le passe à FALSE.
	 */
	public void resetSelection() {
		Enumeration<FormeGeometrique> e = groupeDeFormes.getlesFormes().elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item.isSelected()) {
			item.switchSelected();
		}
	}
	}
	/**
	 * Retourne un boolean qui vaut TRUE si et seulement si la selection est composée d'un seul Groupe.
	 * @return un boolean
	 */
	public boolean isOnlyOneGroupSelected() {
		//ici renvoyer true si la selection est composée seulement d'un groupe
		int compteurGroup =0;
		Groupe grp = getGroupeSelection();
		Enumeration<FormeGeometrique> e = grp.getlesFormes().elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item instanceof Groupe) compteurGroup++;
	}
		int size = grp.getlesFormes().size();
		if (compteurGroup == 1 && size == 1) return true;
		else return false;
	}
	
	
	/**
	 * Permet de grouper la selection.Tout d'abord on vérifie qu'au moins deux forme géométriques sont selectionnées; ensuite
	 * on ajoute le groupe formé par la selection au Groupe du ModeleDessin et enfin on supprime la selection.
	 */
	public void groupSelection() {
		if (howMuchSelected() < 2)
		{
			//erreur, il faut au moins 2 formes selectionnées pour grouper
			JOptionPane.showMessageDialog(null, "You have to select at least 2 items to group them", "Information", JOptionPane.INFORMATION_MESSAGE);
		}
		else{
			//ajout du groupe selectionné aux groupeDeFormes
			this.groupeDeFormes.addForme(this.getGroupeSelection());
			//suppression de la selection
			this.deleteSelection();
			JOptionPane.showMessageDialog(null, "Group item successful", "Information", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	/**
	 * Permet de dégrouper une selection. Tout d'abord on vérifie que la selection est composée seulement d'un groupe; ensuite
	 * on parcourt ce groupe de selection, et on ajoute chaque forme géométrique au Groupe du ModeleDessin. Ensuite on supprime le groupe
	 * de la selection.
	 */
	public void ungroupSelection() {
		if (!isOnlyOneGroupSelected())
		{
			//erreur il faut que la selection soit composée de seulement 1 groupe
			JOptionPane.showMessageDialog(null, "You have to select only one group to ungroup it", "Information", JOptionPane.INFORMATION_MESSAGE);
		}
		else
		{
			//récupérer le groupe unique qui se trouve dans groupeSelection
			Groupe groupeSelection = (Groupe) this.getGroupeSelection().getlesFormes().elementAt(0);
			//Parcourir ce groupe et ajouter toutes les formes au groupeDeFormes
			Enumeration<FormeGeometrique> e = groupeSelection.getlesFormes().elements();
			while (e.hasMoreElements()) {
				FormeGeometrique item = e.nextElement();
				this.groupeDeFormes.addForme(item);
			}
			//et supprimer la selection(le groupe) avec this.deleteSelection
			this.deleteSelection();
			JOptionPane.showMessageDialog(null, "Ungroup item successful", "Information", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	/**
	 * Retourne un Groupe composé des formes selectionnées
	 * @return un Groupe composé des formes selectionnées
	 */
	public Groupe getGroupeSelection() {
		Enumeration<FormeGeometrique> e = groupeDeFormes.getlesFormes().elements();
		Groupe groupFromSelection = new Groupe();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item.isSelected()) {
			groupFromSelection.addForme(item);
		}
	}
		return groupFromSelection;
	}
	
	/**
	 * Permet de retirer du Groupe du ModeleDessin les formes géométriques qui sont selectionnées
	 */
	public void deleteSelection() {
		for (int i=groupeDeFormes.getlesFormes().size()-1;i>=0;i--) {
			if (groupeDeFormes.getlesFormes().elementAt(i).isSelected()) groupeDeFormes.getlesFormes().removeElementAt(i);
		}
		
	}
	
	/**
	 * Permet de déplacer toutes les formes selectionnée en calculant la différence de position entre un point de depart et un point d'arrivée
	 * @param depart
	 * @param destination
	 */
	public void decalerSelection(Point depart, Point destination) {
		Enumeration<FormeGeometrique> e = groupeDeFormes.getlesFormes().elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if(item.isSelected()) 
				{
				
				item.deplacer(depart,destination);
				}
		}
	}
	
	/**
	 * Permet d'appliquer à toutes les formes selectionnées une symétrie par rapport à 0
	 */
	public void symetrie0OnSelection() {
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		for (FormeGeometrique item : vectorgroupe) {
			if (item.isSelected()) item.symetrieO(origine);
		}
		
	}
	/**
	 * Permet d'appliquer à toutes les formes selectionnées une symétrie par rapport à l'axe des X
	 */
	public void symetrieXOnSelection() {
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		for (FormeGeometrique item : vectorgroupe) {
			if (item.isSelected()) item.symetrieX(origine);
		}
		
	}
	
	/**
	 * Permet d'appliquer à toutes les formes selectionnées une symétrie par rapport à l'axe des Y
	 */
	public void symetrieYOnSelection() {
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		for (FormeGeometrique item : vectorgroupe) {
			if (item.isSelected()) item.symetrieY(origine);
		}
		
	}
	
	/**
	 * Permet de dupliquer toutes les formes selectionnées en appliquant une symétrie par rapport à 0
	 */
	public void clone0OnSelection() {
		
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		
		Enumeration<FormeGeometrique> e = vectorgroupe.elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item.isSelected()) {
			FormeGeometrique clone = item.createSymetrieO(origine);
			this.getGroupeDeFormes().addForme(clone);	
		}
}
	}
	
	/**
	 * Permet de dupliquer toutes les formes selectionnées en appliquant une symétrie par rapport à l'axe des Y
	 */
	public void cloneYOnSelection() {
		
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		
		Enumeration<FormeGeometrique> e = vectorgroupe.elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item.isSelected()) {
			FormeGeometrique clone = item.createSymetrieY(origine);
			this.getGroupeDeFormes().addForme(clone);	
		}
}
	}
	/**
	 * Permet de dupliquer toutes les formes selectionnées en appliquant une symétrie par rapport à l'axe des X
	 */
	public void cloneXOnSelection() {
		
		Point origine = Outils.DimensionToOrigine(this.dimensionDessin);
		Vector<FormeGeometrique> vectorgroupe = groupeDeFormes.getlesFormes();
		
		Enumeration<FormeGeometrique> e = vectorgroupe.elements();
		while (e.hasMoreElements()) {
			FormeGeometrique item = e.nextElement();
			if (item.isSelected()) {
			FormeGeometrique clone = item.createSymetrieX(origine);
			this.getGroupeDeFormes().addForme(clone);	
		}
}
	}
	

	
	
}